// Copyright (c) Appeon
// The following has been modified by Appeon under compliance with the Apache 2.0 license from the original work

// Copyright(c) 2002-2019, Npgsql
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written agreement
// is hereby granted, provided that the above copyright notice and this
// paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL NPGSQL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// Npgsql HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// NPGSQL SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE.THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND Npgsql
// HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
// OR MODIFICATIONS.

using System;
using System.Collections;
using System.Linq.Expressions;
using System.Text;
using JetBrains.Annotations;
using NpgsqlTypes;

namespace SnapObjects.Data.PostgreSql
{
    public class PostgreSqlVarbitTypeMapping : PostgreSqlTypeMapping
    {
        public PostgreSqlVarbitTypeMapping([NotNull] string dbTypeName, [NotNull] Type clrType)
            : base(dbTypeName, clrType, NpgsqlDbType.Varbit)
        {
        }

        protected override string GenerateNonNullSqlLiteral(object value)
        {
            var bits = (BitArray)value;

            var sb = new StringBuilder();

            sb.Append("VARBIT B'");

            for (var i = 0; i < bits.Count; i++)
            {
                sb.Append(bits[i] ? '1' : '0');
            }

            sb.Append('\'');

            return sb.ToString();
        }

        public Expression GenerateCodeLiteral(object value)
        {
            var bits = (BitArray)value;
            var exprs = new Expression[bits.Count];
            for (var i = 0; i < bits.Count; i++)
            {
                exprs[i] = Expression.Constant(bits[i]);
            }

            return Expression.New(typeof(BitArray).GetConstructor(new[] { typeof(bool[]) }),
                Expression.NewArrayInit(typeof(bool), exprs));
        }

        protected override bool IsAssignable(Type modelType, Type dbType)
        {
            var isAssignable = base.IsAssignable(modelType, dbType);

            if (!isAssignable && modelType == typeof(bool))
            {
                return true;
            }

            return isAssignable;
        }

        public override object ChangeDbType(object dbValue)
        {
            if (dbValue is bool b)
            {
                return b;
            }

            return base.ChangeDbType(dbValue);
        }

        public override TypeMapping Clone([NotNull] string dbTypeName, int? size)
        {
            return new PostgreSqlVarbitTypeMapping(dbTypeName, this.ClrType);
        }
    }
}
